package org.hsc.proxy;
/*
 * TcpServer.java
 *
 * Created on October 31, 2007
 *
 * Name:        Moty Michaely   |   Alon Halfon
 * ID:          043413087       |   300545969
 */

import java.io.*;
import java.net.*;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Listens for connections from TCP based network clients.
 *
 * @author Moty Michaely
 * @version 1.0
 */
public abstract class TcpServer {
	
	private static final Logger logger=LoggerFactory.getLogger(TcpServer.class);
    /**Holds the address this server listens on. */
    private InetSocketAddress address = null;
    
    /** Determines whether listetning for incoming connection requests should be stopped.*/
    private volatile boolean stopRequested = false;
    
    /** Determines whether this server is listeting for incoming connections.*/
    private volatile boolean isListening = false;
    
    /*
     * The underlying tcp server socket.
     */
    private ServerSocket serverSocket = null;
    
    /** 
     * Creates a new instance of TcpServer 
     * 
     * @param   port    The port for the server to listen for incoming connection requests on.
     */
    protected TcpServer(int port){
        this(new InetSocketAddress(port));
    }
    
    /** 
     * Creates a new instance of TcpServer 
     * 
     * @param   address    The address for the server to listen for incoming connection requests on.
     */
    protected TcpServer(InetSocketAddress address){
        this.address = address;
    }
    
    /*
     * Gets the underlying ServerSocket.
     */
    public synchronized ServerSocket getServerSocket(){
        return this.serverSocket;
    }
    
    /*
     * Gets the value that determines whether this socket is listening for incoming connections.
     */
    public synchronized boolean getIsListening(){
        return this.isListening;
    }
    
    /*
     * Starts this server in a new thread.
     */
    public synchronized void start() throws IOException{
        // Create server socket bound to the specified port.
        this.serverSocket = new ServerSocket(
                this.address.getPort(), 
                0,
                this.address.getAddress());
        
        // Start listening for new incoming connections in a new thread.
        new Thread(new Runnable() {
            public void run() { 
                startListening(); 
            }
        }).start();
    }
    
    /*
     * Starts listening for incoming connection requests asynchronously.
     */
    private void startListening() {
        this.isListening = true;
        try{
            while(!stopRequested){
                try{
                    // Listen for a connection to be made
                    Socket socket = serverSocket.accept();
                    // Handle the new connection
                    accept(socket);
                }catch (SocketException se){
                	logger.error(se.getMessage(),se);
                }
            }
        }catch( Throwable t ){
        	logger.error(t.getMessage(),t);
        }finally{
            try{
                // Close the server socket
                if( this.serverSocket != null )
                    this.serverSocket.close();
            }catch( IOException t ){
            	logger.error(t.getMessage(),t);
            }
            this.isListening = false;
        }        
    }
    
    /*
     * Stops this server by closing the listening socket and disallowing any new connections from being accepted.
     */
    public synchronized void stop() throws IOException
    {
        this.stopRequested = true;
        if( this.serverSocket != null ) {
            this.serverSocket.close();
        }
    }
    
     /*
     * Handles the incoming socket connection.
     * 
     * @param   socket  The accepted connection socket.
     */
    protected abstract void accept(Socket socket);
}
